/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021.
 * Description: support net hooks register
 * Author: douzhaolei
 * Create: 2021-11-15
 */
#ifndef _NET_HOOK_REGISTER_H
#define _NET_HOOK_REGISTER_H

#ifdef CONFIG_RTOS_NET_HOOK_REGISTER
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <net/ip_fib.h>
#include <linux/ip.h>
#ifdef CONFIG_NETFILTER
#include <linux/netfilter/x_tables.h>
#ifdef CONFIG_RTOS_NF_CONNTRACK_CONFIRM_IPV6
#include <net/netfilter/nf_conntrack_core.h>
#endif
#endif
#endif

/* enum NET_HOOK_ACTION represent the action for function
 * int rtos_net_hook(enum NET_HOOK_ACTION action, enum NET_HOOK_FUN fun_type, void *net_hook_fun);
 */
enum NET_HOOK_ACTION {
	NET_HOOK_ACTION_REGISTER,
	NET_HOOK_ACTION_UNREGISTER
};

/* each enum NET_HOOK_FUN represent one net hook function, but NET_HOOK_MAX
 * represent the number of net hook function
 */

enum NET_HOOK_FUN {
	NET_HOOK_G_DAD_CHECK_HOOK,
	NET_HOOK_G_RPS_CHECK_CPU,
#if IS_ENABLED(CONFIG_NETFILTER)
	NET_HOOK_PV_SAVE_PORT_RANGE,
	NET_HOOK_PF_IP_NF_FLOW_LOG_HOOK,
#endif
	NET_HOOK_G_ROUTE_QUERY_ERR,
	NET_HOOK_G_PV_RTV4_PROCESS,
	NET_HOOK_G_PV_SET_NEIGHBOUR_PRIVATEINFO,
	NET_HOOK_G_PV_SET_SOCKET_L2INFO,
#if IS_ENABLED(CONFIG_NETFILTER)
	NET_HOOK_NF_CONNTRACK_CONFIRM_IPV6_HOOK,
#endif
#if IS_ENABLED(CONFIG_IPV6)
	NET_HOOK_G_PV_RT_PROCESS,
	NET_HOOK_G_SHOULD_IGNORE_LINKLOCAL,
	NET_HOOK_P_UDP6_LIB_UCAST_DELIVER,
#endif
	NET_HOOK_G_PV_NBGETHIT_PROCESS,
	NET_HOOK_NB_EVENT_HOOK,
	NET_HOOK_G_PV_FW_ING,
	NET_HOOK_G_PV_FW_EGR,
	NET_HOOK_HW_LOCALOUT_IP_SEND_HOOK,
#if IS_ENABLED(CONFIG_IPV6)
	NET_HOOK_IP6_HW_LOCALOUT_IP_SEND_HOOK,
#endif
	NET_HOOK_SKB_PRIV_COPY,
	NET_HOOK_WIFIPKTFREE,
	NET_HOOK_WIFIPKTADD,
	NET_HOOK_FFWD_IPSEC_RCV,
	NET_HOOK_IPSEC_SENDOUT,
	NET_HOOK_RAW_SOCKET_FILTER,
#if IS_ENABLED(CONFIG_IPV6)
	NET_HOOK_IPV6_RAW_SOCKET_FILTER,
#endif
	NET_HOOK_IGMPV3_SENDPACK_HOOK,
	NET_HOOK_IGMP_SEND_REPORT_HOOK,
#ifdef CONFIG_RTOS_NET_WIFIPKT
	NET_HOOK_WIFIPKTFREEDATA,
#endif
	/* add enum before NET_HOOK_MAX */
	NET_HOOK_MAX
};

extern int (*g_dad_check_hook)(struct net_device *dev,
	const struct in6_addr *solicit,
	const struct in6_addr *saddr);
#ifdef CONFIG_RTOS_NET_DAD_CHECK
static inline bool net_hook_dad_check(struct net_device *dev,
			const struct in6_addr *solicit, const struct in6_addr *saddr)
{
	if ((g_dad_check_hook != NULL) &&
		(*g_dad_check_hook)(dev, solicit, saddr))
			return true;
	return false;
}
#else
static inline bool net_hook_dad_check(struct net_device *dev,
			const struct in6_addr *solicit, const struct in6_addr *saddr)
{
	return false;
}
#endif

extern int (*g_rps_check_cpu)(int cpu);
#ifdef CONFIG_RTOS_NET_RPS_CHECK_CPU
static inline void trace_vendor_rps_check_cpu(int *cpu)
{
	if (g_rps_check_cpu)
		*cpu = (*g_rps_check_cpu)(*cpu);
}
#else
static inline void trace_vendor_rps_check_cpu(int *cpu)
{
}
#endif

#if IS_ENABLED(CONFIG_NETFILTER)
struct xt_action_param;
extern void (*g_pv_save_port_range)(const struct sk_buff *skb,
	struct xt_action_param *par);
#ifdef CONFIG_RTOS_NET_PV_SAVE_PORT_RANGE
static inline void trace_vendor_net_pv_save_port_range(const struct sk_buff *skb,
						struct xt_action_param *par)
{
	if (g_pv_save_port_range)
		(*g_pv_save_port_range)(skb, par);
}
#else
static inline void trace_vendor_net_pv_save_port_range(const struct sk_buff *skb,
						struct xt_action_param *par)
{
}
#endif

struct nf_hook_state;
extern void (*pf_ip_nf_flow_log_hook)(struct sk_buff *pskb,
	struct nf_hook_state *state,
	unsigned int verdict,
	const struct nf_hook_entries *e,
	unsigned int s);
#ifdef CONFIG_RTOS_PF_IP_NF_FLOW_LOG
static inline void trace_vendor_pf_ip_nf_flow_log(struct sk_buff *skb,
				struct nf_hook_state *state, unsigned int verdict,
				const struct nf_hook_entries *e, unsigned int s)
{
	if (pf_ip_nf_flow_log_hook)
		(*pf_ip_nf_flow_log_hook)(skb, state, verdict, e, s);
}
#else
static inline void trace_vendor_pf_ip_nf_flow_log(struct sk_buff *skb,
				struct nf_hook_state *state, unsigned int verdict,
				const struct nf_hook_entries *e, unsigned int s)
{
}
#endif
#endif

#define INPUT_ROUTING    0
#define OUTPUT_ROUTING   1
extern void (*g_route_query_err)(int stage, unsigned short family,
	struct sk_buff *skb, void *flow);
#ifdef CONFIG_RTOS_NET_ROUTE_QUERY_ERR
static inline void trace_vendor_route_query_err_in(struct sk_buff *skb)
{
	if (g_route_query_err)
		(*g_route_query_err)(INPUT_ROUTING,
					AF_INET, skb, NULL);
}
static inline void trace_vendor_route_query_err_out(struct flowi4 *flp4)
{
	if (g_route_query_err)
		(*g_route_query_err)(OUTPUT_ROUTING, AF_INET, NULL, flp4);
}

#else
static inline void trace_vendor_route_query_err_in(struct sk_buff *skb)
{
}
static inline void trace_vendor_route_query_err_out(struct flowi4 *flp4)
{
}
#endif

struct fib_info;
extern void (*g_pv_rtv4_process)(int event, __be32 key,
	int dst_len, u32 tb_id,
	u8 fa_type, struct fib_info *fa_info);
#ifdef CONFIG_RTOS_NET_PV_RTV4_PROCESS
static inline void trace_vendor_net_pv_rtv4_process(int event, __be32 key,
						int dst_len, u32 tb_id,
						u8 fa_type, struct fib_info *fa_info)
{
	if (g_pv_rtv4_process)
		(*g_pv_rtv4_process)(event, key, dst_len,
				tb_id, fa_type, fa_info);
}
#else
static inline void trace_vendor_net_pv_rtv4_process(int event, __be32 key,
						int dst_len, u32 tb_id,
						u8 fa_type, struct fib_info *fa_info)
{
}
#endif

extern void (*g_pv_set_neighbour_privateinfo)(struct sk_buff *skb,
			struct neighbour *neigh);
#ifdef CONFIG_RTOS_NET_PV_SET_NEIGHBOUR_PRIVATEINFO
static inline void trace_vendor_set_neighbour_privateinfo(struct sk_buff *skb,
						struct neighbour *neigh)
{
	if (g_pv_set_neighbour_privateinfo)
		(*g_pv_set_neighbour_privateinfo)(skb, neigh);
}
#else
static inline void trace_vendor_set_neighbour_privateinfo(struct sk_buff *skb,
						struct neighbour *neigh)
{
}
#endif

extern void (*g_pv_set_socket_l2info)(struct sk_buff *skb,
	struct sock *sk,
	struct iov_iter *msg_iter,
	unsigned int len,
	unsigned int *setlen) __read_mostly;
#ifdef CONFIG_RTOS_NET_PV_SET_SOCKET_L2INFO
static inline void trace_vendor_pv_set_socket_l2info(struct sk_buff *skb, struct sock *sk,
				struct msghdr *msg, size_t len, unsigned int *extl2len)
{
	/* hw add: L2Info to socket */
	if (g_pv_set_socket_l2info)
		(*g_pv_set_socket_l2info)(skb, sk, &msg->msg_iter, len, extl2len);
}
#else
static inline void trace_vendor_pv_set_socket_l2info(struct sk_buff *skb, struct sock *sk,
				struct msghdr *msg, size_t len, unsigned int *extl2len)
{
}
#endif

#if IS_ENABLED(CONFIG_NETFILTER)
extern unsigned int (*nf_conntrack_confirm_ipv6_hook)(
	struct sk_buff *skb,
	const struct nf_hook_state *state,
	int conntrack_ret);
#ifdef CONFIG_RTOS_NF_CONNTRACK_CONFIRM_IPV6
static inline unsigned int trace_vendor_nf_conntrack_confirm_ipv6(
				 struct sk_buff *skb,
				 const struct nf_hook_state *state)
{
	unsigned int ret = nf_conntrack_confirm(skb);

	if (nf_conntrack_confirm_ipv6_hook)
		(*nf_conntrack_confirm_ipv6_hook)(skb, state, ret);
	return ret;
}
#else
static inline unsigned int trace_vendor_nf_conntrack_confirm_ipv6(
				 struct sk_buff *skb,
				 const struct nf_hook_state *state)
{
	return 0;
}
#endif
#endif

#if IS_ENABLED(CONFIG_IPV6)
extern void (*g_pv_rt_process)(int event, struct fib6_info *rt);
#ifdef CONFIG_RTOS_NET_PV_RT_PROCESS
static inline void trace_vendor_pv_rt_process(int event, struct fib6_info *rt)
{
	if (g_pv_rt_process)
		(*g_pv_rt_process)(event, rt);
}
#else
static inline void trace_vendor_pv_rt_process(int event, struct fib6_info *rt)
{
}
#endif

extern int (*g_should_ignore_linklocal)(struct net_device *dev);
#ifdef CONFIG_RTOS_NET_IGNORE_LINKLOCAL
static inline bool net_hook_should_ignore_linklocal(struct inet6_dev *idev)
{
	struct net_device *dev = idev->dev;
	if (g_should_ignore_linklocal &&
		(*g_should_ignore_linklocal)(dev))
			return true;
	return false;
}
#else
static inline bool net_hook_should_ignore_linklocal(struct inet6_dev *idev)
{
	return false;
}
#endif

extern int (*p_udp6_lib_ucast_deliver)(struct sock *sk, struct net *net,
	struct sk_buff *skb,
	struct udphdr  *uh,
	struct in6_addr *saddr,
	struct in6_addr *daddr,
	struct udp_table *udptable);
#endif

extern int (*g_pv_nbgethit_process)(struct neighbour *neigh);
#ifdef CONFIG_RTOS_NET_PV_NBGETHIT_PROCESS
static inline int trace_vendor_nbgethit_process(struct neighbour *n)
{
	if (g_pv_nbgethit_process)
		return (*g_pv_nbgethit_process)(n);
	return 0;
}
#else
static inline int trace_vendor_nbgethit_process(struct neighbour *n)
{
	return 0;
}
#endif

extern void (*nb_event_hook)(struct neighbour *neigh);
#ifdef CONFIG_RTOS_NET_FLUSH_NEIGHBOUR
static inline void trace_vendor_nb_event_hook(struct neighbour *neigh)
{
	if (nb_event_hook)
		(*nb_event_hook)(neigh);
}
#else
static inline void trace_vendor_nb_event_hook(struct neighbour *neigh)
{
}
#endif

extern int (*g_pv_fw_ing)(struct sk_buff *skb);

extern int (*g_pv_fw_egr)(struct sk_buff *skb);
#ifdef CONFIG_RTOS_NET_PV_FW
static inline int trace_vendor_dev_queue_xmit(struct sk_buff *skb, struct net_device *dev)
{
	int ret;

	if (g_pv_fw_egr) {
		ret = (*g_pv_fw_egr)(skb);
		if (ret != NET_RX_SUCCESS) {
			atomic_long_inc(&dev->tx_dropped);
			kfree_skb_list(skb);
			return ret;
		}
	}

	return 0;
}
#else
static inline int trace_vendor_dev_queue_xmit(struct sk_buff *skb, struct net_device *dev)
{
	return 0;
}
#endif

extern int (*hw_localout_ip_send_hook)(struct sk_buff *skb);
#ifdef CONFIG_RTOS_NET_LOCALOUT_IP_SEND
static inline bool trace_vendor_hw_localout_ip_send_hook(struct sk_buff *skb)
{
	if (likely(hw_localout_ip_send_hook != NULL) &&
		(*hw_localout_ip_send_hook)(skb) == 0)
		return true;
	return false;
}
#else
static inline bool trace_vendor_hw_localout_ip_send_hook(struct sk_buff *skb)
{
	return false;
}
#endif

#if IS_ENABLED(CONFIG_IPV6)
extern int (*ip6_hw_localout_ip_send_hook)(struct sk_buff *skb);
#ifdef CONFIG_RTOS_NET_LOCALOUT_IP_SEND
static inline bool net_hook_ip6_hw_localout_ip_send(struct sk_buff *skb)
{
	/* ipv6 tcp fast send local out skb */
	if (likely(ip6_hw_localout_ip_send_hook != NULL) &&
		(*ip6_hw_localout_ip_send_hook)(skb) == 0)
		return true;
	return false;
}
#else
static inline bool net_hook_ip6_hw_localout_ip_send(struct sk_buff *skb)
{
	return false;
}
#endif
#endif

extern void (*skb_priv_copy)(struct sk_buff *new, const struct sk_buff *old);
#ifdef CONFIG_RTOS_NET_SKB_PRIV_COPY
static inline void trace_vendor_skb_priv_copy(struct sk_buff *new,
						const struct sk_buff *old)
{
	if (skb_priv_copy)
		(*skb_priv_copy)(new, old);
}
#else
static inline void trace_vendor_skb_priv_copy(struct sk_buff *new,
						const struct sk_buff *old)
{
}
#endif

extern int (*wifipktfree)(struct sk_buff *skb);

#ifdef CONFIG_RTOS_NET_WIFIPKT
extern int (*wifipktfreedata)(struct sk_buff *skb);
#endif

extern void (*wifipktadd)(struct sk_buff *skb);

#ifdef CONFIG_RTOS_NET_WIFIPKT
static inline bool trace_vendor_wifipktfree(struct sk_buff *skb)
{
	if (wifipktfree) {
		if (!(*wifipktfree)(skb))
			return true;
	}
	return false;
}

static inline bool trace_vendor_wifipktfreedata(struct sk_buff *skb)
{
	if (wifipktfreedata) {
		if (!(*wifipktfreedata)(skb))
			return true;
	}
	return false;
}

static inline void trace_vendor_wifipktadd(struct sk_buff *skb)
{
	if (wifipktadd)
		(*wifipktadd)(skb);
}
#else
static inline bool trace_vendor_wifipktfree(struct sk_buff *skb)
{
	return false;
}

static inline bool trace_vendor_wifipktfreedata(struct sk_buff *skb)
{
	return false;
}

static inline void trace_vendor_wifipktadd(struct sk_buff *skb)
{
}
#endif

extern int (*ffwd_ipsec_rcv_hook)(struct sk_buff *skb);
#ifdef CONFIG_RTOS_NET_IPSEC_BYPASS_NETFILTER
static inline bool trace_vendor_ffwd_ipsec_rcv(struct sk_buff *skb)
{
	if ((ffwd_ipsec_rcv_hook != NULL) &&
		(*ffwd_ipsec_rcv_hook)(skb) == NET_RX_SUCCESS)
			return true;
	return false;
}
#else
static inline bool trace_vendor_ffwd_ipsec_rcv(struct sk_buff *skb)
{
	return false;
}
#endif

extern int (*ipsec_sendout_hook)(struct sk_buff *skb);
#ifdef CONFIG_RTOS_NET_IPSEC_BYPASS_NETFILTER
static inline bool trace_vendor_ipsec_sendout_hook(struct sk_buff *skb)
{
	if (likely(ipsec_sendout_hook) &&
	    (ipsec_sendout_hook(skb) == NET_RX_SUCCESS))
		return true;
	return false;
}
#else
static inline bool trace_vendor_ipsec_sendout_hook(struct sk_buff *skb)
{
	return false;
}
#endif

extern int (*ipv4_raw_socket_filter_hook)(struct sk_buff *skb, const struct iphdr *iph, int hash);
#ifdef CONFIG_RTOS_NET_RAW_SOCKET_FILTER
static inline bool net_hook_ipv4_raw_socket_filter(struct sk_buff *skb,
						  const struct iphdr *iph, int hash)
{
	if (ipv4_raw_socket_filter_hook != NULL &&
		(*ipv4_raw_socket_filter_hook)(skb, iph, hash) == 1)
			return true;
	return false;
}
#else
static inline bool net_hook_ipv4_raw_socket_filter(struct sk_buff *skb,
						  const struct iphdr *iph, int hash)
{
	return false;
}
#endif

extern int (*ipv6_raw_socket_filter_hook)(struct sk_buff *skb, int nexthdr);
#ifdef CONFIG_RTOS_NET_RAW_SOCKET_FILTER
static inline bool net_hook_ipv6_raw_socket_filter(struct sk_buff *skb, int nexthdr)
{
	if (ipv6_raw_socket_filter_hook != NULL &&
		(*ipv6_raw_socket_filter_hook)(skb, nexthdr) == 1)
			return true;
	return false;
}
#else
static inline bool net_hook_ipv6_raw_socket_filter(struct sk_buff *skb, int nexthdr)
{
	return false;
}
#endif

extern int (*igmpv3_sendpack_hook)(struct sk_buff *skb);
#ifdef CONFIG_RTOS_IGMP_NOTIFIER
static inline bool trace_vendor_igmpv3_sendpack_hook(struct sk_buff *skb)
{
	if (igmpv3_sendpack_hook != NULL &&
		(*igmpv3_sendpack_hook)(skb) == NET_RX_DROP) {
			kfree_skb(skb);
			return true;
	}
	return false;
}
#else
static inline bool trace_vendor_igmpv3_sendpack_hook(struct sk_buff *skb)
{
	return false;
}
#endif

extern int (*igmp_send_report_hook)(struct sk_buff *skb);
#ifdef CONFIG_RTOS_IGMP_NOTIFIER
static inline bool trace_vendor_igmp_send_report_hook(struct sk_buff *skb)
{
	if (igmp_send_report_hook != NULL &&
		(*igmp_send_report_hook)(skb) == NET_RX_DROP) {
			kfree_skb(skb);
			return true;
	}
	return false;
}
#else
static inline bool trace_vendor_igmp_send_report_hook(struct sk_buff *skb)
{
	return false;
}
#endif

struct net_hook_fun {
	enum NET_HOOK_FUN type;
	void **p_hook_fun;
};
/* end of net hook function prototype declearation */

/* use this function to register net hook function */
int rtos_net_hook(enum NET_HOOK_ACTION action, enum NET_HOOK_FUN fun_type, void *p_net_hook_fun);

/* use this function to get net hook's value */
void *rtos_net_hook_get(enum NET_HOOK_FUN fun_type);

#endif
